/*
 *GreyBox was written by Strategic Data Systems and licenced under the open source MS-PL. 
 */
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Net;
using System.Security.Cryptography.X509Certificates;
using System.Xml.Linq;
using StrategicDataSystems.Cloud.Monitoring.HostedServices.ResponseToEntityMappers;
using System.Security;
using System.Diagnostics;
using System.Threading;

namespace StrategicDataSystems.Cloud.Monitoring.HostedServices
{
    public class HostedServiceManagementGateway : IHostedServiceManagement
    {

        public IEnumerable<HostedService> GetHostedServicesForSubscription(string subscriptionId, string certificateThumbprint)
        {
            Trace.WriteLine("GetHostedServicesForSubscription called.", "System");
            string targetAddress = String.Format(@"https://management.core.windows.net/{0}/services/hostedservices", subscriptionId);
            HttpWebRequest request = createManagementApiRequest(targetAddress, certificateThumbprint, "GET");

            HttpWebResponse response = getResponse(request);
            XDocument responseXml = loadResponseStreamIntoXml(response);

            HostedServiceListResponseMapper responseMapper = new HostedServiceListResponseMapper();
            IEnumerable<string> hostedServiceUrls = responseMapper.MapToEntity(responseXml);

            IEnumerable<HostedService> hostedServices = from serviceUrl in hostedServiceUrls
                                                        select getHostedServiceProperties(serviceUrl, certificateThumbprint);

            return hostedServices.ToList();
        }

        public void KillHostedServiceDeployment(string subscriptionId, string certificateThumbprint, string hostedServiceName, string slot)
        {
            string suspendRequestId = suspendHostedServiceDeployment(subscriptionId, certificateThumbprint, hostedServiceName, slot);
            waitForOperationToComplete(subscriptionId, certificateThumbprint, suspendRequestId);
            Trace.WriteLine(string.Format("Operation completed! {0}", DateTime.Now.ToString()));
            deleteHostedServiceDeployment(subscriptionId, certificateThumbprint, hostedServiceName, slot);
        }

        private string suspendHostedServiceDeployment(string subscriptionId, string certificateThumbprint, string hostedServiceName, string slot)
        {
            string targetAddress = String.Format(@"https://management.core.windows.net/{0}/services/hostedservices/{1}/deploymentslots/{2}/?comp=status",
                subscriptionId, hostedServiceName, slot);

            HttpWebRequest request = createManagementApiRequest(targetAddress, certificateThumbprint, "POST");

            string requestPayload = "<?xml version='1.0' encoding='utf-8'?><UpdateDeploymentStatus xmlns='http://schemas.microsoft.com/windowsazure'><Status>Suspended</Status></UpdateDeploymentStatus>";
            request.ContentLength = requestPayload.Length;
            request.ContentType = "application/xml";
            using (Stream requestStream = request.GetRequestStream())
            {
                using (StreamWriter requestBodyWriter = new StreamWriter(requestStream))
                {
                    requestBodyWriter.Write(requestPayload);
                }
            }
            var response = getResponse(request);
            return response.Headers["x-ms-request-id"];
        }

        private void deleteHostedServiceDeployment(string subscriptionId, string certificateThumbprint, string hostedServiceName, string slot)
        {
            string targetAddress = String.Format(@"https://management.core.windows.net/{0}/services/hostedservices/{1}/deploymentslots/{2}",
                subscriptionId, hostedServiceName, slot);

            HttpWebRequest request = createManagementApiRequest(targetAddress, certificateThumbprint, "DELETE");

            var response = getResponse(request);
        }

        private void waitForOperationToComplete(string subscriptionId, string certificateThumbprint, string operationId)
        {
            HostedServiceOperationStatus operationStatus = getOperationStatus(subscriptionId, certificateThumbprint, operationId);
            while (operationStatus.Status == OperationStatus.InProgress)
            {
                Thread.Sleep(new TimeSpan(0, 0, 3));
                operationStatus = getOperationStatus(subscriptionId, certificateThumbprint, operationId);
            }
            Trace.WriteLine(string.Format("Operation {0} has completed with a status of {1}.", operationId, operationStatus.Status));
        }

        private HostedService getHostedServiceProperties(string serviceUrl, string certificateThumbprint)
        {
            HttpWebRequest request = createManagementApiRequest(string.Concat(serviceUrl, "?embed-detail=true"), certificateThumbprint, "GET");

            var response = getResponse(request);
            XDocument responseXml = loadResponseStreamIntoXml(response);

            HostedServicePropertiesResponseMapper mapper = new HostedServicePropertiesResponseMapper();
            HostedService service = mapper.MapToEntity(responseXml);

            return service;

        }

        private static HttpWebRequest createManagementApiRequest(string targetAddress, string certificateThumbprint, string httpMethod)
        {
            HttpWebRequest request = (HttpWebRequest)HttpWebRequest.Create(targetAddress);

            request.Headers.Add("x-ms-version", "2010-04-01");
            request.Method = httpMethod;
            request.ContentLength = 0;
            request.ClientCertificates.Add(getCertificateFromLocalStore(certificateThumbprint));
            return request;
        }


        private static X509Certificate getCertificateFromLocalStore(string certificateThumbprint)
        {
            var certStore = new X509Store();
            certStore.Open(OpenFlags.ReadOnly);

            var certificates = (from X509Certificate2 cert in certStore.Certificates
                                where cert.Thumbprint == certificateThumbprint
                                select cert);

            if (certificates.Any())
            {
                X509Certificate managementCert = certificates.First();

                return managementCert;
            }
            else
            {
                throw new ArgumentException("Could not locate the Certificate in your Local My Certificate store with the thumbprint provided.");
            }
        }

        private XDocument loadResponseStreamIntoXml(HttpWebResponse response)
        {
            Stream responseStream = response.GetResponseStream();

            XDocument responseXml = XDocument.Load(new StreamReader(responseStream));
            return responseXml;
        }

        private HostedServiceOperationStatus getOperationStatus(string subscriptionId, string certificateThumbprint, string operationId)
        {
            string targetAddress = String.Format(@"https://management.core.windows.net/{0}/operations/{1}",
                             subscriptionId, operationId);

            HttpWebRequest request = createManagementApiRequest(targetAddress, certificateThumbprint, "GET");

            var response = (HttpWebResponse)request.GetResponse();
            XDocument responseXml = loadResponseStreamIntoXml(response);
            OperationStatusResponseMapper mapper = new OperationStatusResponseMapper();
            HostedServiceOperationStatus operationStatus = mapper.MapToEntity(responseXml);
            return operationStatus;
        }

        private HttpWebResponse getResponse(HttpWebRequest request)
        {
            try
            {
                var response = (HttpWebResponse)request.GetResponse();
                return response;
            }
            catch (System.Net.WebException webException)
            {
                //TODO: Verify that the webException.Respose is not null, or that means that it failed to actually reach the server.
                if (((System.Net.HttpWebResponse)(webException.Response)).StatusCode == HttpStatusCode.Forbidden)
                {
                    throw new SecurityException("Access to the management API was forbidden. Please check your subscriptionID and Thumbprint are correct.");
                }
                else
                {
                    throw;
                }
            }
        }

    }
}
